package block

import (
	"bytes"
	"encoding/gob"
	pow2 "publicChainSystem/pow"
	"publicChainSystem/tools"
	"publicChainSystem/transaction"
	"strconv"
	"time"
)

/**
 *@author:zhengyilong
 *@email:1797344574@qq.com
 *@phone:13479394730
 *@DateTime:2022/2/21 20:20
 **/

type Block struct {
	PrevHash []byte		//上一个区块的哈希值
	TimeStamp int64		//时间戳
	//Data []byte			//交易信息
	Txs []transaction.Transaction
	Nonce int64			//随机数
	Hash []byte			//当前区块的哈希值
}

func (block *Block) GetTimeStamp() int64 {
	return block.TimeStamp
}
func (block *Block) GetPrevHash() []byte {
	return block.PrevHash
}
func (block *Block) GetTxs() []transaction.Transaction {
	return block.Txs
}

/*
创建区块
*/
func NewBlock(prevHash []byte,txs []transaction.Transaction) *Block {
	block := Block{
		PrevHash: prevHash,
		TimeStamp: time.Now().Unix(),
		//Data: data,
		Txs:txs,
	}
	//传一个实现了blockinterface的结构体
	pow := pow2.NewPow(&block)
	hash, nonce := pow.Run()
	block.Hash = hash
	block.Nonce = nonce

	return &block
}

/*
序列化	把目标结构体转成一个有序的序列
	json	xml
 */
func (block *Block) Serialize() ([]byte,error){
	var result bytes.Buffer
	en := gob.NewEncoder(&result)
	err := en.Encode(block)
	if err != nil {
		return nil,err
	}
	return result.Bytes(),nil
}

/*
反序列化		把字节切片转成结构体
 */
func DeSerialize(data []byte) (*Block,error){
	reader := bytes.NewReader(data)
	de := gob.NewDecoder(reader)
	var block *Block
	err := de.Decode(&block)
	if err != nil {
		return nil,err
	}
	return block,nil
}

/*
获取当前区块的哈希值
*/
func (block *Block) SetHash() ([]byte,error) {
	//当前区块的hash值是由时间戳，上一个区块的hash+交易信息+随机数 形成一个字符串，计算这个字符串的hash值

	time := []byte(strconv.FormatInt(block.TimeStamp, 10))
	nonce := []byte(strconv.FormatInt(block.Nonce, 10))
	//将每一个交易信息序列化成字节切片后追加到一个大的字节切片中
	txsBytes := []byte{}
	for _, value := range block.Txs {
		serialize, err := value.Serialize()
		if err != nil {
			return nil, err
		}
		txsBytes = append(txsBytes, serialize...)
	}

	//bytes.Join	拼接		第一个人参数 存放拼接的内容	第二个参数 以什么形式进行拼接
	hash := bytes.Join([][]byte{block.PrevHash, txsBytes, time,nonce}, []byte{})

	return tools.GetHash(hash),nil
}

//创建创世区块
func GenesisBlock(tx transaction.Transaction) *Block{
	return NewBlock(nil,[]transaction.Transaction{tx})
}